<?php
require_once "Gi_Exception.php";
  /**
  * @desc Gi_Loader: Clase para simplificar carga de archivos e basa En la clase Zend_Loader de la version 1.5 del Zend Framework
  */
class Gi_Loader {
  /**
  * @desc Carga una clase o Interface en base a su nombre de archivo
  * @param string Nombre de la clase
  * @param string || Array Opcional Directorios en los que buscar
  */
  public static function load_class($class, $dirs = null, $modulo = false) {
    //se verifica si esta cargada la clase o la interface de ser asi vuelve
    if (class_exists($class, false) || interface_exists($class, false)) {
      return;
    }
    
    //verificamos que trae el parametro dir
    if ((null !== $dirs) && !is_string($dirs) && !is_array($dirs)) {
      //si no trae un valor correcto excepcion
      throw new Gi_Exception('El parametro $dirs debe ser tipo string o tipo array');
    }
    //las clases del framework se nombran Gi_Nombre-clase_[vacio || Abstract || Interface || Exception] segun corresponda es lo que busca esta funcion 
    $name = explode("_",$class);
    
    //cada tipo de clase va en la carpeta que le corresponde y se busca ahi primero
    if ( isset ($name[2]) ) {
      switch( $name[2] ) {
        case "Abstract" :
        case "Exception":
        case "Interface":
          $file = "$name[2]" . DIRECTORY_SEPARATOR . "$class" . ".php";
          break;
        default : 
          $file = "$class" . ".php";
      }
    }
    //si no tiene el subigo se busca directamente en lib
    else {
      $file = "$class" . ".php";
    }
    
    if ($modulo) {
      $file = ereg_replace(ucfirst($modulo) . "_","", $file);
    }
    
    if (!empty($dirs)) {
      // chequea si se mando el aprametro file con directorios
      $dir_path = dirname($file);
      if (is_string($dirs)) {
        $dirs = explode(PATH_SEPARATOR, $dirs);
      }
      foreach ($dirs as $key => $dir) {
        if ($dir == '.') {
          $dirs[$key] = $dir_path;
        } 
        else {
          $dir = rtrim($dir, '\\/');
          $dirs[$key] = $dir . DIRECTORY_SEPARATOR . $dir_path;
        }
      }
      $file = basename($file);
      
      //luego de obtener el nombre de archivo seguro, lo carga
      self::load_file($file, $dirs, true);
    } 
    else {
      //chequea el nombre de archivo y lo carga
      self::load_file($file);
    }
    //si sigue sin existir la clase o interface Excepcion
    if (!class_exists($class, false) && !interface_exists($class, false)) {
      throw new Gi_Exception("El archivo \"$file\" no existe o la clase \"$class\" no fue encontrada en el archivo");
    }
  }
  /**
  * @desc Carga un archivo similar al include de php pero con validaciones
  * @param string Nombre del Archivo
  * @param string | array opcional directorios donde buscar
  * @param bool opcional para saber si se hace un include_once o no por default no
  */
  public static function load_file($filename, $dirs = null, $once = false) {
    //chequeo de nombre de archivo
    self::_security_check($filename);
    
    // busca en los direcotorios provistos y tambien en el includepath
    $inc_path = false;
    if (!empty($dirs) && (is_array($dirs) || is_string($dirs))) {
      if (is_array($dirs)) {
        $dirs = implode(PATH_SEPARATOR, $dirs);
      }
      $inc_path = get_include_path();
      set_include_path($dirs . PATH_SEPARATOR . $inc_path . PATH_SEPARATOR);
    }

    /**
     * trata de cargar los archivos
     */
     
    if ($once) {
      include_once $filename;
    } 
    else {
      include $filename;
    }

    /**
     *reset de include path si se cmabio
     */
    if ($inc_path) {
      set_include_path($inc_path);
    }

    return true;
  } 
  
  /**
  * @desc Chequea que un archivo sea legible
  * @param string El Nombre del Archivo en cuestion
  */
  public static function is_readable($filename) {
    if (!$fh = @fopen($filename, 'r', true)) {
      return false;
    }
    @fclose($fh);
    return true;
  }
  /**
  * @desc Funcion de auto carga para objetos del Framework, en general para cualqueir objeto que cumpla la norma de nombres de clases del framework, no es necesario llamar a esta funcion se puede usar load_class o directamente register_autoload y todas laas clases seran cargadas cuando sea necesario
  * @param El nombre de la calse a cargar
  */
  public static function autoload($class) {
    try {
      self::load_class($class);
      return $class;
    } 
    catch (Exception $e) {
      return false;
    }
  }
  
  /**
  * @desc regista la funcion autoload en el stack de la Spl_autoload_register
  * @param string Opcional nombre de la clase que tiene un metodo llamado autoload, esto se hace asi por si acaso se quiere hacer otra clase que tenga un metodo autoload, asi no hay necesidad de extender esta solo pasarle a esta funcion el nombre de clase con la funcion autoload
  * @param bool Opcional esta para metro indica si se registra la funcion autoload y se borra por default true y la carga al ponerlo en false la borra del stack
  */
  public static function register_autoload($class = 'Gi_Loader', $enabled = true) {
    if (!function_exists('spl_autoload_register')) {
      
      throw new Gi_Exception('spl_autoload no existe en esta instalacion de php');
    }

    self::load_class($class);
    $methods = get_class_methods($class);
    if (!in_array('autoload', (array) $methods)) {
      //nunca deberia entrar aca a no ser que se llame de forma incorrecta
      throw new Gi_Exception("la clase \"$class\" no tiene un metodo autoload");
    }

    if ($enabled === true) {
      spl_autoload_register(array($class, 'autoload'));
    } 
    else {
      spl_autoload_unregister(array($class, 'autoload'));
    }
  }
  
  /*FUnciones Protected*/
   /**
   * @desc funcion para chequear nombre de archivos legales
   * @param string nombre de archivo
   */
   protected static function _security_check($filename) {
  /**
   * Chequeo de seguridad
   */
    if (preg_match('/[^a-z0-9\\/\\\\_.-]/i', $filename)) {
      
      throw new Exception('Security check: Illegal character in filename');
    }
  }
}